package com.start.core.config;

import com.start.common.details.BaseUserDetails;
import com.start.common.entity.StartBaseEntity;
import com.start.security.StartTokenTools;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Map;

@Slf4j
@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class StartMyBatisInterceptor implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        String sqlId = mappedStatement.getId();
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        Object parameter = invocation.getArgs()[1];
        try {
            injectionData(sqlCommandType, parameter);
        } catch (Exception e) {
            log.debug("sqlCommandType=" + sqlCommandType + ",", e);
        }
        return invocation.proceed();
    }

    private void injectionData(SqlCommandType sqlCommandType, Object parameter) throws Exception {
        if (parameter instanceof StartBaseEntity) {
            handlerBaseEntity(sqlCommandType, parameter);

        } else if (parameter instanceof Map) {
            handlerMap(sqlCommandType, parameter);
        }

    }

    private void handlerMap(SqlCommandType sqlCommandType, Object parameter) {
        BaseUserDetails currentUser = getCurrentUser();
        if (currentUser == null) return;
        Map entity = (Map) parameter;
        switch (sqlCommandType) {
            case INSERT:
                handlerKeyValue(entity, currentUser.getUserId(), "createBy", "create_by");
                handlerKeyValue(entity, LocalDateTime.now(), "createTime", "create_time");
                break;
            case UPDATE:
                handlerKeyValue(entity, currentUser.getUserId(), "updateBy", "update_by");
                handlerKeyValue(entity, LocalDateTime.now(), "updateTime", "update_by");
                break;
        }

    }

    private void handlerKeyValue(Map map, Object value, String... keys) {
        if (keys == null || keys.length < 1) return;
        for (String key : keys) {
            if (!map.containsKey(key)) {
                continue;
            }
            Object keyValue = map.getOrDefault(key, null);
            if (keyValue != null) {
                return;
            }
            map.replace(key, value);
            return;
        }

    }

    private void handlerBaseEntity(SqlCommandType sqlCommandType, Object parameter) {
        BaseUserDetails currentUser = getCurrentUser();
        if (currentUser == null) return;
        StartBaseEntity entity = (StartBaseEntity) parameter;
        switch (sqlCommandType) {
            case INSERT:
                if (StringUtils.isEmpty(entity.getCreateBy())) {
                    entity.setCreateBy(currentUser.getUserId());
                }
                if (entity.getCreateTime() == null) {
                    entity.setCreateTime(LocalDateTime.now());
                }

                break;
            case UPDATE:
                if (StringUtils.isEmpty(entity.getUpdateBy())) {
                    entity.setUpdateBy(currentUser.getUserId());
                }
                if (entity.getUpdateTime() == null) {
                    entity.setUpdateTime(LocalDateTime.now());
                }
                break;
        }
    }

    private BaseUserDetails getCurrentUser() {
        try {
            return StartTokenTools.getCurrentUser();
        } catch (Exception e) {
            return null;
        }

    }
}
